你能用 AspectJ 替换抽象 class 吗

Can you replace an abstract class with AspectJ

给定 java.util.Timer 你可以这样做:

// where TimerMock extends Timer
Timer around(): call (Timer.new()) {
    return new TimerMock();
}

这可能与抽象 classes 有关吗?以 java.util.TimerTask 为例,它是具有以下签名的 abstract class:

public abstract class TimerTask implements Runnable {
    (...)
    public abstract void run();
}

是否可以 return 扩展 TimerTask 的 class?

如何?

您将很难正确匹配连接点。

让我用你最初的例子来解释问题:

public aspect InterceptTimer
{
    // where TimerMock extends Timer
    Timer around(): call(Timer.new())
    {
         return new TimerMock();
    }
}

public class Test {

    public void method() 
    {
        Timer a = new Timer();
        System.out.println(a.toString());
    }
}

编织后,AspectJ 将替换 Timer a = new Timer(); 以替换语义上等同于 Timer a = new TimerMock(); 的东西。

然而,有了摘要class,事情就没那么顺利了。在这种情况下,您想用具体 class 替换抽象 class 的具体实现。这样做的一个方面可能类似于(假设现在 TimerMock 扩展了 TimerTask):

public aspect InterceptTimer
{
    // where TimerMock extends Timer
    TimerTask around(): call(TimerTask+.new()) && !within(InterceptTimer)
    {
        return new TimerMock();
    }  
}

"TimerTask+" 确保拦截所有扩展 TimerTask 的 class,"!within(InterceptTimer)" 确保切入点不拦截连接点 "new TimerMock();" 这将导致无限递归。

现在假设您有一个具体的 class 这样:

public class MyTimer extends TimerTask {

    @Override
    public void run() 
    {
       // ...
    }
}  

如果要拦截以下创建:

public void method() {
    MyTimer a = new MyTimer();
    a.run();
} 

您会立即收到错误消息: “ 不兼容 return 类型应用于构造函数调用(void test.MyTimer.()) “

那是因为连接点 "new MyTimer()" 在词法上与 "TimerTask" 不同。如果您可以通过 "new TimerTask()" 更改 "new MyTimer()" 连接点,它会起作用,但是,您不能这样做,因为 TimerTask 是抽象的 class.

如果你想变聪明,把"advice"改成:Object around(): call(MyTask+.new()) && !within(InterceptTimer){...}

您不会得到编译错误,而是运行时错误:

"java.lang.ClassCastException: test.TimerMock cannot be cast to test.MyTimer"

这是因为在编织过程中 AspectJ 将执行以下操作:MyTimer a = (MyTimer) new TimerMock();

我认为不可能做你想做的事,至少不能用那种方法,也许有一种方法可以利用 "execution" 和 "call" 连接点之间的差异来做到这一点.