【发布时间】:2015-02-02 15:54:10
【问题描述】:
在 Java 中的 AOP (AspectJ) 中,当我们谈论方法切入点时,我们可以将它们区分为两个不同的集合:method call pointcuts 和 method execution pointcuts。
基于 SO 上的这些资源:
还有一些AspectJ的背景,我们可以看出两者的区别基本上可以表达如下:
鉴于这些类:
class CallerObject {
//...
public void someMethod() {
CompiletimeTypeObject target = new RuntimeTypeObject();
target.someMethodOfTarget();
}
//...
}
class RuntimeTypeObject extends CompileTypeObject {
@Override
public void someMethodOfTarget() {
super.someMethodOfTarget();
//...some other stuff
}
}
class CompiletimeTypeObject {
public void someMethodOfTarget() {
//...some stuff
}
}
- 方法调用切入点是指调用者对象调用目标方法的方法 /em> 对象(实际实现被调用方法的对象)。在上面的例子中,调用者是CallerObject,目标是RuntimeTypeObject。另外,方法调用切入点是指对象的编译时类型,即上例中的“CompiletimeTypeObject”;
所以method call pointcut 是这样的:
pointcut methodCallPointcut():
call(void com.example.CompiletimeTypeObject.someMethodOfTarget())
会匹配CallerObject.someMethod()方法内部的target.someMethodOfTarget();连接点,因为RuntimeTypeObject的compile type是CompiletimeTypeObject,但是这个方法调用切入点:
pointcut methodCallPointcut():
call(void com.example.RuntimeTypeObject.someMethodOfTarget())
不匹配,因为对象的编译时类型 (CompiletimeTypeObject) 不是 RuntimeTypeObject 或它的子类型(相反)。
- 方法执行切入点指的是方法的执行(即在方法已被调用或之前 方法调用返回)。它不提供有关调用者的信息,更重要的是它指的是对象的运行时类型,而不是编译时类型。
所以,这两个方法执行切入点都将匹配target.someMethodOfTarget(); 执行连接点:
pointcut methodCallPointcut():
execution(void com.example.CompiletimeTypeObject.someMethodOfTarget())
pointcut methodCallPointcut():
execution(void com.example.RuntimeTypeObject.someMethodOfTarget())
因为匹配是基于对象的运行时类型,两者都是 RuntimeTypeObject,而 RuntimeTypeObject 既是 CompiletimeTypeObject(第一个切入点)又是一个 RuntimeTypeObject(第二个切入点)。
现在,由于 PHP 不提供对象的编译时类型(除非使用类型提示以某种方式模拟这种行为),在 PHP AOP 实现中区分方法调用和方法执行切入点是否有意义?那么这些切入点之间会有什么不同呢?
感谢关注!
编辑:@kriegaex 指出了 AspectJ 中调用和方法执行切入点之间的另一个有趣方面。
感谢您提供的简洁明了的示例。我自己也尝试过举个例子,这就是我的理解:
在案例A(我使用第3方库)中,我实际上无法拦截库方法的执行,因为库本身已经编译成字节码以及与相关的任何方面该库也已经编织到该字节码中(我需要编织源代码才能这样做)。
所以我只能拦截对库方法的方法调用,但我只能在我的代码中拦截对库方法的调用而不是由于相同的原理,从库本身调用库方法(从库本身对库方法的调用也已经编译)。
这同样适用于系统类(原理相同),正如这里所说的(即使引用是指 JBoss):
https://docs.jboss.org/jbossaop/docs/2.0.0.GA/docs/aspect-framework/reference/en/html/pointcuts.html
系统类不能在执行表达式中使用,因为它 无法对它们进行检测。
如果 B(我为其他用户提供库),如果我真的需要在库本身或将来使用该方法的用户代码中拦截我的库的方法的使用,那么我需要使用执行切入点作为切面编织器将编译关注我的库的方法执行和调用切入点,而不是将使用我的库方法的用户代码(仅仅是因为在我编写库时用户代码还不存在),因此使用执行切入点将确保编织将发生在方法执行内部(对于一个清晰直观的示例,请查看@kriegaex 下面的伪代码),而不是在我的库中调用该方法的任何位置(即在调用者端)。
因此,当在我的库中和用户代码中使用该方法时,我可以拦截我的库方法的使用(更准确地说,执行)。 如果我在这种情况下使用了方法调用切入点,我将只截获 从我的库中进行的调用,而不是在用户代码中进行的调用。
不管怎样,如果这些考虑是有意义的并且可以应用于 PHP 世界,你们觉得呢?
【问题讨论】: