【问题标题】:ProceedingJoinPoint.proceed() fails with ClassCastException when run on new thread在新线程上运行时,ProceedingJoinPoint.proceed() 失败并出现 ClassCastException
【发布时间】:2015-06-26 01:20:01
【问题描述】:

我正在设置和@Around aspect 在后台线程上运行一个方法,它看起来像这样

@Aspect
public class ThreadAspect {    
    @Around("call(@Background void *(..))")
    public void runInBackground(final ProceedingJoinPoint jp) throws Throwable {
        new Thread(new JPRunner(jp)).start();
    }

    private static class JPRunner implements Runnable {
        ...
        @Override
        public void run() {
            try {
                jp.proceed();
            } catch (Throwable e) {
                Log.e("TEST", "ThreadAspect", e);
            }
        }
    }
}

我在一个采用String 的方法上应用了@Background 注释,但它与ClassCastExceptionjp.proceed() 的行上失败了

E/TEST    (20943): java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint

有趣的是,如果我不使用线程,调用似乎可以顺利通过。如何让它在线程上运行?

如果重要的话,我在 android 上使用 aspectj 和 this plugin

编辑:这是失败的代码

// Background.java
package com.github.larvyde.ex.aspect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Background {}

// MainActivity.java
package com.github.larvyde.ex.aspect;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends android.support.v7.app.AppCompatActivity {
    @Override
    public void onCreate(Bundle saved) {
        super.onCreate(saved);

        Log.v("TEST", "calling runInBackground");
        runInBackground("run #1");
        Log.v("TEST", "calling runInBackground again");
        runInBackground("run #2");
    }

    @Background
    public void runInBackground(String str) {
        Log.v("TEST", str);
    }
}

// ThreadAspect.java
package com.github.larvyde.ex.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import android.util.Log;

@Aspect
public class ThreadAspect {
    @Around("call(@Background void *(..))")
    public void runInBackground(ProceedingJoinPoint jp) throws Throwable {
        new Thread(new JPRunner(jp)).start();
    }

    private static class JPRunner implements Runnable {
        private final ProceedingJoinPoint jp;

        public JPRunner(ProceedingJoinPoint jp) {
            this.jp = jp;
        }

        @Override
        public void run() {
            try {
                jp.proceed();
            } catch (Throwable e) {
                Log.e("TEST", "ThreadAspect", e);
            }
        }
    }
}

日志

$ adb logcat | egrep 'TEST|AndroidRuntime'
V/TEST    (21315): calling runInBackground
V/TEST    (21315): calling runInBackground again
E/TEST    (21315): ThreadAspect
E/TEST    (21315): java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint
E/TEST    (21315):  at com.github.larvyde.ex.aspect.MainActivity$AjcClosure1.run(MainActivity.java:1)
E/TEST    (21315):  at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
E/TEST    (21315):  at com.github.larvyde.ex.aspect.ThreadAspect$JPRunner.run(ThreadAspect.java:25)
E/TEST    (21315):  at java.lang.Thread.run(Thread.java:818)
E/TEST    (21315): ThreadAspect
E/TEST    (21315): java.lang.ClassCastException: java.lang.String cannot be cast to org.aspectj.lang.JoinPoint
E/TEST    (21315):  at com.github.larvyde.ex.aspect.MainActivity$AjcClosure3.run(MainActivity.java:1)
E/TEST    (21315):  at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149)
E/TEST    (21315):  at com.github.larvyde.ex.aspect.ThreadAspect$JPRunner.run(ThreadAspect.java:25)
E/TEST    (21315):  at java.lang.Thread.run(Thread.java:818)

【问题讨论】:

    标签: android aop aspectj


    【解决方案1】:

    对我来说,这很好用,也许您的实际代码与您在此处发布的代码不同,或者您省略了一些重要信息。看看我的独立示例:

    标记注释:

    package de.scrum_master.app;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Background {}
    

    驱动程序应用:

    如您所见,一种方法被注解标记,另一种则没有。

    package de.scrum_master.app;
    
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    
    public class Application {
        static final DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    
        public static void foreground() {
            System.out.println(
                dateFormat.format(Calendar.getInstance().getTime()) +
                " - synchronous call"
            );
        }
    
        @Background
        public static void background() {
            System.out.println(
                dateFormat.format(Calendar.getInstance().getTime()) +
                " - asynchronous call"
            );
        }
    
        public static void main(String[] args) {
            foreground();
            background();
            foreground();
            background();
        }
    }
    

    没有 AspectJ 的控制台输出:

    18:21:09 - synchronous call
    18:21:09 - asynchronous call
    18:21:09 - synchronous call
    18:21:09 - asynchronous call
    

    如您所见,这些方法是按照它们被调用的顺序记录的,并且都具有相同的时间戳。

    方面在自己的线程中异步运行标记的方法:

    这几乎是您的方面代码,但我插入了 2 秒的等待时间以演示 @Background 效果。

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    
    @Aspect
    public class ThreadAspect {
        @Around("call(@de.scrum_master.app.Background void *(..))")
        public void runInBackground(final ProceedingJoinPoint jp) throws Throwable {
            new Thread(new JPRunner(jp)).start();
        }
    
        private static class JPRunner implements Runnable {
            ProceedingJoinPoint jp;
            JPRunner(ProceedingJoinPoint jp) { this.jp = jp; }
    
            @Override public void run() {
                try { Thread.sleep(2000); jp.proceed(); }
                catch (Throwable e) { e.printStackTrace(); }
            }
        }
    }
    

    使用 AspectJ 进行控制台输出:

    18:23:21 - synchronous call
    18:23:21 - synchronous call
    18:23:23 - asynchronous call
    18:23:23 - asynchronous call
    

    正如您在此处看到的,两个异步调用(后台任务)都在同步调用后 2 秒打印。

    【讨论】:

    • 不,它几乎是从我的代码中复制粘贴过来的。嗯,我看到你在常规 java 上测试过,所以我想这可能是我正在使用的 android aspectj 插件的问题......
    • 我不这么认为。请提供SSCCE 以使问题可重现并证明相反。如果我能重现它,我可以帮助你。
    • 在普通的 Eclipse AspectJ 项目中编译时,您的确切代码是否可以在 Android 以外的台式 PC 上运行?不过,您需要将 Android 日志调用替换为 System.out.println(..) 或其他日志框架。
    • 还有一个问题:您使用哪种Java语言级别和哪个AspectJ版本?
    猜你喜欢
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多