禁用/避免在AspectJ中执行建议

eme*_*esx 9 java aop aspectj

假设我有一个方面

public aspect Hack {

pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass);

boolean around(String user, String pass): authHack(user,pass) {
    out("$$$ " + user + ":" + pass + " $$$");
    return false;
}

}
Run Code Online (Sandbox Code Playgroud)

这种Authenticator.authenticate方法很重要.黑客拦截对此方法的调用.

是否有可能编写第二个方面来取消/禁用authHackHack方面的建议?

我可以捕获around authHack建议的执行,但如果我想继续身份验证,我需要Authenticator.authenticate再次调用,这会创建一个无限循环..

Yan*_*eve 9

为了模拟您的情况,我编写了以下Authenticator代码:

public class Authenticator {

    public boolean authenticate(String user, String pass) {
        System.out.println("User: '" + user + "', pass: '" + pass + "'");
        return true;
    }

}
Run Code Online (Sandbox Code Playgroud)

这是我的主要课程:

public class Main {

    public static void main(String[] args) {

        Authenticator authenticator = new Authenticator();

        boolean status = authenticator.authenticate("Yaneeve", "12345");
        System.out.println("Status: '" + status + "'");
    }

}
Run Code Online (Sandbox Code Playgroud)

输出是:

User: 'Yaneeve', pass: '12345'
Status: 'true'
Run Code Online (Sandbox Code Playgroud)

我添加了你的Hack方面:

public aspect Hack {

    pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass);

    boolean around(String user, String pass): authHack(user,pass) {
        System.out.println("$$$ " + user + ":" + pass + " $$$");
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在的输出是:

$$$ Yaneeve:12345 $$$
Status: 'false'
Run Code Online (Sandbox Code Playgroud)

现在为解决方案:

我创建了以下HackTheHack方面:

public aspect HackTheHack {

    declare precedence: "HackTheHack", "Hack";

    pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass);

    boolean around(String user, String pass): authHack(user,pass) {
        boolean status = false;
        try {
            Class<?> klass = Class.forName("Authenticator");
            Object newInstance = klass.newInstance();
            Method authMethod = klass.getDeclaredMethod("authenticate", String.class, String.class);
            status = (Boolean) authMethod.invoke(newInstance, user, pass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return status;
    }
}
Run Code Online (Sandbox Code Playgroud)

再次输出:

User: 'Yaneeve', pass: '12345'
Status: 'true'
Run Code Online (Sandbox Code Playgroud)

这只有在Hack方面的原始切入点是'call'而不是'execution'时才有效,因为执行实际上会捕获反射.

说明:

在使用Hack之前,我使用了Aspect优先级来调用HackTheHack:

declare precedence: "HackTheHack", "Hack";
Run Code Online (Sandbox Code Playgroud)

然后,我使用了反射(注意可以并且应该进行优化以减少方法的重复查找),以简单地调用原始方法,而无需使用Hack around建议.由于两件事情,这已成为可能:

  1. authHack切入点:pointcut authHack(String user, String pass): call(* Authenticator.authenticate(String,String)) && args(user,pass);使用(在两个方面)call()而不是execution()
  2. 我没有打电话给proceed()HackTheHack

我想请你参考Manning的AspectJ in Action,第二版,它让我走上正轨:

6.3.1建议的排序

正如您刚才所见,系统中存在多个方面,不同方面的建议通常可以应用于单个连接点.发生这种情况时,AspectJ使用以下优先级规则来确定应用建议的顺序.稍后,您将看到如何控制优先级:

1具有较高优先级的方面在具有较低优先级的方面之前对连接点执行其之前的建议.

2具有较高优先级的方面在具有较低优先级的方面之后对连接点执行其后建议.

3较高优先级方面的around建议包含较低优先级方面的around建议.这种安排允许较高优先级方面通过控制对proceed()的调用来控制是否将运行较低优先级的建议.如果优先级较高的方面不在其建议体中调用proceed(),则不仅低优先级方面不会执行,而且建议的连接点也不会执行.