aspectJ中的cflow能否检测跨线程执行?
Can cflow in aspectJ detect cross-thread execution?
问题
我想在 运行 所有调用方法
之前打印出请求 url 和响应
public class UpdateRequester {
private void throwMessage(String requestUrl, String page) {
//Some code inside
}
}
将在测试中调用的方法class:
public class Test {
public void testUpdate() {
Executors.scheduleWithFixedDelay(new Runnable() {
public void run() {
//It will call throwMessage sometimes in the future
}
}, ...);
}
}
所以我设计了一个方面:
public aspect TestUpdate {
static final void println(String s) {
System.out.println(s);
}
pointcut testUpdateFlow() : cflow(this(Test) && execution(void testUpdate()));
pointcut throwMessageCut(String url, String response) : this(UpdateRequester) && args(url, response) && execution(void throwMessage(String, String));
before(String url, String response) : testUpdateFlow() && throwMessageCut( url, response) {
println("=============Url============");
println(url);
println("============Respnse=========");
println(response);
}
}
该方面不向控制台打印任何内容。如果我删除 testUpdateFlow(),它会打印到控制台。
我认为 aspectJ 中的 cflow 不会将 Executors.scheduleWithFixedDelay
的代码 运行 视为在 testUpdate()
的流程中。在这种情况下,有什么方法可以让 aspectJ 检测线程交叉调用吗?
让我们假设我们有这些 classes:
package de.scrum_master.app;
public class UpdateRequester {
public void doSomething() {
throwMessage("http://my.url.org/foo", "my page");
}
private void throwMessage(String requestUrl, String page) {
System.out.println("Throwing message for request " + requestUrl + " on page '" + page + "'");
}
}
因为throwMessage(..)
在你的例子中是私有的,所以我特意添加了一个public方法doSomething()
,可以被测试调用class,我还添加了一个main(..)
方法作为我测试的入口点:
package de.scrum_master.app;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test {
public void testUpdate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
executorService.scheduleWithFixedDelay(
new Runnable() {
public void run() {
new UpdateRequester().doSomething();
}
},
500L,
1000L,
TimeUnit.MILLISECONDS
);
}
public static void main(String[] args) {
new Test().testUpdate();
}
}
现在让我们从我们的 before()
建议中打印异常堆栈跟踪,以找出控制流的真正含义:
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
public aspect TestUpdate {
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
/*testUpdateFlow() &&*/
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
您会看到这样的堆栈跟踪:
execution(void de.scrum_master.app.UpdateRequester.throwMessage(String, String))
java.lang.Exception
at de.scrum_master.app.TestUpdate.ajc$before$de_scrum_master_app_TestUpdatefbc0c(TestUpdate.aj:16)
at de.scrum_master.app.UpdateRequester.throwMessage(UpdateRequester.java:9)
at de.scrum_master.app.UpdateRequester.doSomething(UpdateRequester.java:5)
at de.scrum_master.app.Test.run(Test.java:13)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Throwing message for request http://my.url.org/foo on page 'my page'
即this()
不是 Test
,而是 Test
,它是您在代码中定义的匿名内部 Runnable
subclass。您还会看到 Test.testUpdate()
并不真正在控制流中,因为它在堆栈跟踪中无处可见。您可以像这样修改切入点:
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
withincode(public void Test..*.run(..)) &&
call(* UpdateRequester.*(..))
);
表示:在
的控制流程中
Runnable
、 的实例
- 在下面定义的
public void run(..)
方法的代码中某处 Test
(内部 class),
- 调用
UpdateRequester
的任何方法。
即现在方面看起来像这样(控制台输出保持不变):
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
import java.lang.Runnable;
public aspect TestUpdate {
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
withincode(public void Test..*.run(..)) &&
call(* UpdateRequester.*(..))
);
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
testUpdateFlow() &&
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
您还可以使用嵌套的 cflow()
语句,如下所示:
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
cflow(execution(public void Test..*.run(..))) &&
call(* UpdateRequester.*(..))
);
或者,为了避免匿名内部 class 你可以创建一个命名内部 class:
package de.scrum_master.app;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test {
public static class UpdateRequesterStarter implements Runnable {
public void run() {
new UpdateRequester().doSomething();
}
}
public void testUpdate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
executorService.scheduleWithFixedDelay(
new UpdateRequesterStarter(),
500L,
1000L,
TimeUnit.MILLISECONDS
);
}
public static void main(String[] args) {
new Test().testUpdate();
}
}
现在输出改变了,请注意调用堆栈的不同:
execution(void de.scrum_master.app.UpdateRequester.throwMessage(String, String))
java.lang.Exception
at de.scrum_master.app.TestUpdate.ajc$before$de_scrum_master_app_TestUpdatec6f966b(TestUpdate.aj:24)
at de.scrum_master.app.UpdateRequester.throwMessage(UpdateRequester.java:9)
at de.scrum_master.app.UpdateRequester.doSomething(UpdateRequester.java:5)
at de.scrum_master.app.Test$UpdateRequesterStarter.run(Test.java:10)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Throwing message for request http://my.url.org/foo on page 'my page'
现在您可以 refine/simplify testUpdateFlow()
切入点:
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
import de.scrum_master.app.Test.UpdateRequesterStarter;
public aspect TestUpdate {
pointcut testUpdateFlow() :
cflow(execution(public void UpdateRequesterStarter.run(..)));
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
testUpdateFlow() &&
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
另请注意更改后的 import
语句。
问题
我想在 运行 所有调用方法
之前打印出请求 url 和响应public class UpdateRequester {
private void throwMessage(String requestUrl, String page) {
//Some code inside
}
}
将在测试中调用的方法class:
public class Test {
public void testUpdate() {
Executors.scheduleWithFixedDelay(new Runnable() {
public void run() {
//It will call throwMessage sometimes in the future
}
}, ...);
}
}
所以我设计了一个方面:
public aspect TestUpdate {
static final void println(String s) {
System.out.println(s);
}
pointcut testUpdateFlow() : cflow(this(Test) && execution(void testUpdate()));
pointcut throwMessageCut(String url, String response) : this(UpdateRequester) && args(url, response) && execution(void throwMessage(String, String));
before(String url, String response) : testUpdateFlow() && throwMessageCut( url, response) {
println("=============Url============");
println(url);
println("============Respnse=========");
println(response);
}
}
该方面不向控制台打印任何内容。如果我删除 testUpdateFlow(),它会打印到控制台。
我认为 aspectJ 中的 cflow 不会将 Executors.scheduleWithFixedDelay
的代码 运行 视为在 testUpdate()
的流程中。在这种情况下,有什么方法可以让 aspectJ 检测线程交叉调用吗?
让我们假设我们有这些 classes:
package de.scrum_master.app;
public class UpdateRequester {
public void doSomething() {
throwMessage("http://my.url.org/foo", "my page");
}
private void throwMessage(String requestUrl, String page) {
System.out.println("Throwing message for request " + requestUrl + " on page '" + page + "'");
}
}
因为throwMessage(..)
在你的例子中是私有的,所以我特意添加了一个public方法doSomething()
,可以被测试调用class,我还添加了一个main(..)
方法作为我测试的入口点:
package de.scrum_master.app;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test {
public void testUpdate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
executorService.scheduleWithFixedDelay(
new Runnable() {
public void run() {
new UpdateRequester().doSomething();
}
},
500L,
1000L,
TimeUnit.MILLISECONDS
);
}
public static void main(String[] args) {
new Test().testUpdate();
}
}
现在让我们从我们的 before()
建议中打印异常堆栈跟踪,以找出控制流的真正含义:
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
public aspect TestUpdate {
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
/*testUpdateFlow() &&*/
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
您会看到这样的堆栈跟踪:
execution(void de.scrum_master.app.UpdateRequester.throwMessage(String, String))
java.lang.Exception
at de.scrum_master.app.TestUpdate.ajc$before$de_scrum_master_app_TestUpdatefbc0c(TestUpdate.aj:16)
at de.scrum_master.app.UpdateRequester.throwMessage(UpdateRequester.java:9)
at de.scrum_master.app.UpdateRequester.doSomething(UpdateRequester.java:5)
at de.scrum_master.app.Test.run(Test.java:13)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Throwing message for request http://my.url.org/foo on page 'my page'
即this()
不是 Test
,而是 Test
,它是您在代码中定义的匿名内部 Runnable
subclass。您还会看到 Test.testUpdate()
并不真正在控制流中,因为它在堆栈跟踪中无处可见。您可以像这样修改切入点:
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
withincode(public void Test..*.run(..)) &&
call(* UpdateRequester.*(..))
);
表示:在
的控制流程中Runnable
、 的实例
- 在下面定义的
public void run(..)
方法的代码中某处Test
(内部 class), - 调用
UpdateRequester
的任何方法。
即现在方面看起来像这样(控制台输出保持不变):
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
import java.lang.Runnable;
public aspect TestUpdate {
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
withincode(public void Test..*.run(..)) &&
call(* UpdateRequester.*(..))
);
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
testUpdateFlow() &&
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
您还可以使用嵌套的 cflow()
语句,如下所示:
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
cflow(execution(public void Test..*.run(..))) &&
call(* UpdateRequester.*(..))
);
或者,为了避免匿名内部 class 你可以创建一个命名内部 class:
package de.scrum_master.app;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test {
public static class UpdateRequesterStarter implements Runnable {
public void run() {
new UpdateRequester().doSomething();
}
}
public void testUpdate() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
executorService.scheduleWithFixedDelay(
new UpdateRequesterStarter(),
500L,
1000L,
TimeUnit.MILLISECONDS
);
}
public static void main(String[] args) {
new Test().testUpdate();
}
}
现在输出改变了,请注意调用堆栈的不同:
execution(void de.scrum_master.app.UpdateRequester.throwMessage(String, String))
java.lang.Exception
at de.scrum_master.app.TestUpdate.ajc$before$de_scrum_master_app_TestUpdatec6f966b(TestUpdate.aj:24)
at de.scrum_master.app.UpdateRequester.throwMessage(UpdateRequester.java:9)
at de.scrum_master.app.UpdateRequester.doSomething(UpdateRequester.java:5)
at de.scrum_master.app.Test$UpdateRequesterStarter.run(Test.java:10)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.runAndReset(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(Unknown Source)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Throwing message for request http://my.url.org/foo on page 'my page'
现在您可以 refine/simplify testUpdateFlow()
切入点:
package de.scrum_master.app;
import de.scrum_master.app.UpdateRequester;
import de.scrum_master.app.Test.UpdateRequesterStarter;
public aspect TestUpdate {
pointcut testUpdateFlow() :
cflow(execution(public void UpdateRequesterStarter.run(..)));
pointcut throwMessageCut(String url, String response) :
this(UpdateRequester) &&
args(url, response) &&
execution(void throwMessage(String, String));
before(String url, String response) :
testUpdateFlow() &&
throwMessageCut(url, response)
{
System.out.println(thisJoinPoint);
new Exception().printStackTrace(System.out);
}
}
另请注意更改后的 import
语句。