目标:使用AspectJ在执行特定方法后调用静态方法。
为了解决这个问题,我们把静态方法称为System.out.println
,方法称为onConfigurationChanged
。
约束条件:
onConfigurationChanged
方法在我无法控制(无法编织)* 的类中声明、实现和调用。- 由于
onConfigurationChanged
在基类中有一个实现,因此子类可能会也可能不会覆盖它(但在这两种情况下,onConfigurationChanged
执行后仍应调用System.out.println
)。 - 使用注释语法,因为我的开发环境中似乎不支持本机方面语法。
*注意:这是在构建Android应用程序的上下文中,因此所讨论的基类实际上是android.app.Activity
。很明显,编译时编织是不可能的。 我已经研究了加载时编织,但我有点不确定在这种情况下如何完成它,也不确定我是否愿意,因为它是如此关键的代码路径。
我目前面临的主要问题是子类不覆盖该方法的情况。
我尝试过:
-
用
onConfigurationChanged
签名指定的执行切入点,定义了@After
建议,调用System.out.println
。@After("execution(void com.jkhong..*.onConfigurationChanged()) " + "&& !within(DefaultOnConfigurationChangedAspect)") public void onConfigurationChangedExecution() { doDefaultOnConfigurationChanged(); } private static void doDefaultOnConfigurationChanged() { System.out.println("Default onConfigurationChanged (mixed in)"); }
上述方法适用于子类覆盖onConfigurationChanged
方法的情况。
-
@DeclareMixin
以子类为目标,返回调用System.out.println
的匿名类实现。返回的接口具有与基类中onConfigurationChanged
完全相同的签名的单个方法。public interface OnConfigurationChangedListener { void onConfigurationChanged(); } @DeclareMixin("com.jkhong..*.*Activity") public static OnConfigurationChangedListener createDefaultListener() { return new OnConfigurationChangedListener() { @Override public void onConfigurationChanged() { doDefaultOnConfigurationChanged(); } }; }
不幸的是,上面没有将方面提供的onConfigurationChanged
实现添加到不覆盖onConfigurationChanged
的子类中。 它确实指定子类实现OnConfigurationChangedListener
接口,但由于该方法是在其父类中实现的,因此编译器不会抱怨。 (如果我稍微调整接口方法的签名,使其不再匹配,我会看到它确实被添加,但这不是想要的结果。
非常感谢任何帮助,谢谢。
出于技术原因,您必须使用本机语法declare parents
因为它比@DeclareParents
或@DeclareMixin
更强大。
Eclipse 和 IntelliJ IDEA 都支持 AspectJ 原生语法。不过,我不知道 NetBeans。您使用哪个 IDE?在Eclipse中找到对高级AspectJ语法功能的最佳支持,IDEA有一些缺点。但是忽略语法突出显示、代码完成和重构等不错的功能,您可以在任何编辑器中编写一个方面。
至于你的问题,我想强调一下,我不是Android开发人员,所以我只是重新创建了你在这两个虚拟类中使用的基本Android API功能,以使我的方面可测试:
将这些放入一个普通的Java项目中(或使用原始的Android API):
package android.app;
import android.content.res.Configuration;
public class Activity {
public void onConfigurationChanged (Configuration newConfig) {
System.out.println("onConfigurationChanged - default implementation");
}
}
package android.content.res;
public class Configuration {}
现在我们在 IDE 中创建另一个项目,这次是一个 AspectJ 项目。它应该引用上面的小虚拟Android API仿真。
在 AspectJ 项目中,我们创建了两个Activity
子类进行演示,一个覆盖onConfigurationChanged
,一个不覆盖它:
package com.jkhong.app;
import android.app.Activity;
import android.content.res.Configuration;
public class OverridingActivity extends Activity {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
System.out.println("onConfigurationChanged - subclass override");
}
}
package com.jkhong.app;
import android.app.Activity;
public class NonOverridingActivity extends Activity {}
通过一个小的驱动程序应用程序,我们可以显示会发生什么:
package com.jkhong.app;
import android.app.Activity;
import android.content.res.Configuration;
public class Application {
public static void main(String[] args) {
Configuration newConfig = new Configuration();
System.out.println("Activity");
new Activity().onConfigurationChanged(newConfig);
System.out.println("nOverridingActivity");
new OverridingActivity().onConfigurationChanged(newConfig);
System.out.println("nNonOverridingActivity");
new NonOverridingActivity().onConfigurationChanged(newConfig);
}
}
控制台日志如下所示:
Activity
onConfigurationChanged - default implementation
OverridingActivity
onConfigurationChanged - default implementation
onConfigurationChanged - subclass override
NonOverridingActivity
onConfigurationChanged - default implementation
如您所见,OverridingActivity
在执行任何其他操作之前调用其super
方法。我建议你总是这样做。对于NonOverridingActivity
,仅按预期执行基类的默认实现。
现在这是解决您问题的方面:
package com.jkhong.aspect;
import android.app.Activity;
import android.content.res.Configuration;
public aspect ConfigurationChangeInterceptor {
public static class DefaultChangeListener extends Activity {
@Override
public void onConfigurationChanged (Configuration newConfig) {
super.onConfigurationChanged(newConfig);
doDefaultOnConfigurationChanged();
}
}
declare parents : Activity+ && !Activity extends DefaultChangeListener;
private static void doDefaultOnConfigurationChanged() {
System.out.println("onConfigurationChanged - aspect override");
}
}
请注意该方面如何使用Activity+ && !Activity
来指定"所有子类Activity
但不是Activity
自身",以避免编译器错误,因为Activity
无法扩展自身,并且无论如何都不会暴露给编织者。
控制台日志现在如下所示:
Activity
onConfigurationChanged - default implementation
OverridingActivity
onConfigurationChanged - default implementation
onConfigurationChanged - aspect override
onConfigurationChanged - subclass override
NonOverridingActivity
onConfigurationChanged - default implementation
onConfigurationChanged - aspect override
所以现在你得到了你想要的。它为 NonOverridingActivity
和 OverridingActivity
触发的日志输出。