AspectJ中的cflow是否可以检测到跨线程执行? [英] Can cflow in aspectJ detect cross-thread execution?

查看:78
本文介绍了AspectJ中的cflow是否可以检测到跨线程执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题

我想在运行方法的所有调用之前打印出请求的网址和响应

I want to print out the request url and response before running all calls to method

public class UpdateRequester {
   private void throwMessage(String requestUrl, String page) {
      //Some code inside
   }
}

将在Test类中调用该方法:

the method will be called is in the Test class:

public class Test {
  public void testUpdate() {
    Executors.scheduleWithFixedDelay(new Runnable() {
        public void run() {
          //It will call throwMessage sometimes in the future
        }
    }, ...);
  }  
}

所以我设计了一个方面:

so I designed an aspect:

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来检测线程交叉调用吗?

The aspect doesn't print anything to the console. If I remove testUpdateFlow(), it did print to the console. I think that cflow in aspectJ doesn't consider the code running by Executors.scheduleWithFixedDelay to be in the flow of testUpdate(). Is there any way I can use for aspectJ to detect thread-crossing call in this situation?

推荐答案

让我们假设我们具有以下类:

Let us assume we have these 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(..)是私有的,所以我故意添加了一个公共方法doSomething(),该方法可以由测试类调用,我还向其中添加了main(..)方法作为测试的入口点:

Because throwMessage(..) is private in your example, I deliberately added a public method doSomething() which can be called by the test class to which I also added a main(..) method as an entry point for my test:

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()建议中打印一个异常堆栈跟踪,以了解控制流的真正含义是:

Now let us print an exception stack trace from our before() advice in order to find out what the control flow really is:

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

您会看到这样的堆栈跟踪:

You see a stack trace like this:

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()并没有真正出现在控制流中,因为在堆栈跟踪中无处可见.您可以像这样修改切入点:

I.e. that this() is not Test but Test$1 which is the anonymous inner Runnable subclass you define in your code. You also see that Test.testUpdate() is not really in the control flow as it is nowhere to be seen in the stack trace. You can modify the pointcut like this:

pointcut testUpdateFlow() :
    cflow(
        this(Runnable) &&
        withincode(public void Test..*.run(..)) &&
        call(* UpdateRequester.*(..))
    );

这意味着:在控制流程中

It means: In the control flow of

  • Runnable的实例,
  • Test(内部类)下面定义的public void run(..)方法的代码中的某个位置,
  • 调用UpdateRequester的任何方法.
  • an instance of Runnable,
  • somewhere within the code of a public void run(..) method defined below Test (inner class),
  • calling any method of UpdateRequester.

即现在方面看起来像这样(控制台输出保持不变):

I.e. now the aspect looks like this (the console output stays the same):

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()语句,如下所示:

You could also use nested cflow() statements like this:

pointcut testUpdateFlow() :
    cflow(
        this(Runnable) &&
        cflow(execution(public void Test..*.run(..))) &&
        call(* UpdateRequester.*(..))
    );

或者,为了避免匿名内部类,您可以创建一个命名内部类:

Alternatively, in order to avoid the anonymous inner class you can create a named inner 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();
    }
}

现在输出已更改,请注意调用堆栈中的区别:

Now the output changes, please note the difference in the call stack:

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()切入点优化/简化为:

Now you can refine/simplify the testUpdateFlow() pointcut to:

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语句.

Please also note the changed import statements.

这篇关于AspectJ中的cflow是否可以检测到跨线程执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆