问题
我想在运行对方法
的所有调用之前打印出请求url和响应public class UpdateRequester {
private void throwMessage(String requestUrl, String page) {
//Some code inside
}
}
该方法将在Test类中调用:
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来检测穿越线程的呼叫吗?
答案 0 :(得分:2)
让我们假设我们有这些类:
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(..)
在您的示例中是私有的,所以我故意添加了一个公共方法doSomething()
,可以由测试类调用,我还在其中添加了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_TestUpdate$1$33fbc0c(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$1.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.access$301(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$1
,它是您在代码中定义的匿名内部Runnable
子类。您还会看到Test.testUpdate()
实际上并不在控制流中,因为在堆栈跟踪中无处可见。您可以像这样修改切入点:
pointcut testUpdateFlow() :
cflow(
this(Runnable) &&
withincode(public void Test..*.run(..)) &&
call(* UpdateRequester.*(..))
);
这意味着:在
的控制流程中Runnable
,public void run(..)
(内部类)下面定义的Test
方法的代码中的某处,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.*(..))
);
或者,为了避免使用匿名内部类,您可以创建一个命名的内部类:
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_TestUpdate$1$9c6f966b(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.access$301(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'
现在,您可以将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
语句。