PHP中调用和执行切入点之间的区别? [英] Difference between call and execution pointcuts in PHP?

查看:80
本文介绍了PHP中调用和执行切入点之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Java的AOP(AspectJ)中,当我们谈论方法切入点时,我们可以将它们分为两个不同的集合:method call pointcutsmethod execution pointcuts.

In AOP in Java (AspectJ) when we talk about method pointcuts, we can differentiate them into two different sets: method call pointcuts and method execution pointcuts.

在SO上基于这些资源:

Basing on these resources here on SO:

  • execution Vs. call Join point
  • Difference between call and execution in AOP

在某些AspectJ背景下,我们可以说基本上可以将两者之间的差异表示为:

And some AspectJ background, we can tell that basically the differences between the two can be expressed as the following:

给出以下类别:

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
    }
}

  • 方法调用切入点是指从 caller 对象调用方法的 call ,该对象调用 target 对象(实际实现所调用方法的对象).在上面的示例中,调用者为 CallerObject ,目标为 RuntimeTypeObject .另外,方法调用切入点是指对象的编译时类型,即上述示例中的"CompiletimeTypeObject";
    • A method call pointcut refers to the call of a method from a caller object which calls the method of a target object (the one which actually implements the method being called). In the example above, the caller is CallerObject, the target is RuntimeTypeObject. Also, a method call pointcut refers to the compile time type of the object, i.e. "CompiletimeTypeObject" in the example above;
    • 所以method call pointcut是这样的:

      pointcut methodCallPointcut(): 
         call(void com.example.CompiletimeTypeObject.someMethodOfTarget())
      

      将匹配CallerObject.someMethod()方法内的target.someMethodOfTarget();连接点,因为RuntimeTypeObject的compile type是CompiletimeTypeObject,但是此方法调用切入点:

      Will match the target.someMethodOfTarget(); join point inside the CallerObject.someMethod() method as the compile type of the RuntimeTypeObject is CompiletimeTypeObject, but this method call pointcut:

      pointcut methodCallPointcut(): 
         call(void com.example.RuntimeTypeObject.someMethodOfTarget())
      

      将不匹配,因为对象的编译时间类型(CompiletimeTypeObject)不是RuntimeTypeObject或其子类型(相反).

      Will not match, as the compile time type of the object (CompiletimeTypeObject) is not a RuntimeTypeObject or a subtype of it (it is the opposite).

      • 方法执行切入点是指方法的执行(即,在方法被调用后 或在之前方法调用返回).它不提供有关调用方的信息,更重要的是,它引用对象的运行时类型,而不是编译时类型.
      • A method execution pointcut refers to the execution of a method (i.e. after the method has been called or right before the method call returns). It doesn't give information about the caller and more important it refers to the runtime type of the object and not to the compile time type.

      因此,这两个方法执行切入点都将匹配target.someMethodOfTarget();执行连接点:

      So, both these method execution pointcuts will match the target.someMethodOfTarget(); execution join point:

      pointcut methodCallPointcut(): 
             execution(void com.example.CompiletimeTypeObject.someMethodOfTarget())
      
      pointcut methodCallPointcut(): 
             execution(void com.example.RuntimeTypeObject.someMethodOfTarget())
      

      由于匹配基于对象的运行时类型,两者均为RuntimeTypeObject,而RuntimeTypeObject均为CompiletimeTypeObject(第一个切入点)和RuntimeTypeObject(第二个切入点).

      As the matching is based on the runtime type of the object which is RuntimeTypeObject for both and RuntimeTypeObject is both CompiletimeTypeObject (first pointcut) and a RuntimeTypeObject (second pointcut).

      现在,由于PHP不提供对象的编译时类型(除非使用类型提示来模仿这种行为),在PHP AOP实现中区分方法调用和方法执行切入点有意义吗?切入点之间会有什么不同?

      Now, as PHP doesn't provide compile time types for objects (unless type-hinting is used to somehow emulate this behaviour), does it make sense to differentiate method call and method execution pointcuts in a PHP AOP implementation? How then will the pointcuts differ from each other?

      感谢您的关注!

      @kriegaex指出了AspectJ中调用和方法执行切入点之间的另一个有趣方面.

      @kriegaex has pointed out another interesting aspect between call and method execution pointcuts in AspectJ.

      感谢您提供简洁明了的示例.我也尝试自己做一个例子,这就是我的理解:

      Thank you for the great and concise example. I have tried to make an example myself too and here is what I understood:

      在案例A(我使用的是第三方库)中,我实际上无法拦截库方法的执行,因为该库本身已经被编译为字节码以及与有关的任何方面该库也已经被编入到该字节码中了(为此,我需要编织源代码).

      In case A (I use a 3rd party library), I actually can't intercept the execution of a library method because the library itself was already compiled into bytecode and any aspect concerning that library was already woven into that bytecode too (I would need to weave the sources in order to do so).

      因此,我只能截获对库方法的方法调用,但是我只能截取对我代码中的库方法的调用 ,而不能截获由于相同的原理(从库本身对库方法的调用也已编译),因此从库自身内部对库方法的调用.

      So I can only intercept the method calls to the library methods, but again I can only intercept the calls to library methods in my code and not the calls to library methods from within the library itself because of the same principle (the calls to library methods from within the library itself are also already compiled).

      与此处所说的系统类(原则相同)相同(即使引用引用的是JBoss):

      The same applies for System classes (same principle) as is said here (even if the reference refers to JBoss):

      https://docs.jboss.org/jbossaop/docs/2.0.0.GA/docs/aspect-framework/reference/en/html/pointcuts.html

      系统类不能在执行表达式中使用,因为它 无法对它们进行检测.

      System classes cannot be used within execution expressions because it is impossible to instrument them.

      在情况B(我为其他用户提供了一个库)中,如果我实际上需要在库本身或将来使用该方法的用户代码中拦截我的库方法的使用,则我需要使用执行切入点作为方面编织者,将编译关注我的库所关注的方法执行和调用切入点,而不是将使用我的库方法的用户代码(仅因为用户代码在我编写库时尚不存在),因此使用执行切入点将确保在方法执行的内部中进行编织(对于一个清晰直观的示例,请参见@kriegaex伪代码,而不是在我的库中调用该方法的任何地方(即在调用方).

      In case B (I provide a library for other users), if I actually need to intercept the usage of a method of my library either in the library itself or in the future user code which will use that method, then I need to use an execution pointcut as the aspect weaver will compile both the method execution and call pointcuts that concern my library and not the user code which will use my library methods (simply because the user code doesn't exist yet when I am writing the library), therefore using an execution pointcut will ensure that the weaving will occur inside the method execution (for a clear and intuitive example, look at the @kriegaex pseudo-code below) and not wherever the method is called within my library (i.e. at the caller side).

      因此,当在库中和用户代码中使用库方法时,我都可以拦截该方法的使用(更确切地说,是执行). 如果在这种情况下使用了方法调用切入点,那么我只会截获从库中 发出的调用,而不会截获用户代码中的调用.

      So I can intercept the usage (more precisely, execution) of my library method both when the method is used within my library and in the user's code. If I had used a method call pointcut in this case, I would have intercepted only the calls made from within my library, and not the calls made in the user's code.

      无论如何,仍然认为如果这些考虑是合理的并且可以在PHP世界中应用,那么您认为呢?

      Anyway, still think if these considerations make sense and can be applied in the PHP world, what do you think guys?

      推荐答案

      免责声明:我一点也不讲PHP.因此,我的回答本质上是笼统的,而不是特定于PHP的.

      Disclaimer: I do not speak PHP, not even a little. So my answer is rather general in nature than specific to PHP.

      AFAIK,PHP是一种解释性语言,而不是一种编译语言.因此,区别不在于编译时间与运行时类型,而是在语义上与实际类型相比.我想象一个基于PHP的AOP框架不会编译"任何东西,而是预处理源代码,将额外的(方面)源代码注入到原始文件中.也许仍然可以以某种方式将声明的类型与实际的类型区分开.

      AFAIK, PHP is an interpreted rather than a compiled language. So the difference is not compile time vs. runtime type, but semantically rather declared vs. actual type. I imagine that a PHP-based AOP framework would not "compile" anything but rather preprocess source code, injecting extra (aspect) source code into the original files. Probably it would still be possible to differentiate declared from actual types somehow.

      但是还有另一个重要因素也与callexecution连接点之间的差异有关:代码的编织位置.想象一下您使用库或自己提供库的情况.每个给定情况的问题是,在应用方面编织时,源代码的哪些部分在用户的控制之下.

      But there is another important factor which is also relevant to the difference between call vs execution joinpoints: The place in which the code is woven. Imagine situations in which you use libraries or provide them by yourself. The question for each given situation is which parts of the source code is under the user's control when applying aspect weaving.

      • 案例A:您使用第三方库:让我们假设您不能(或不想)将方面编织到库中.然后,您不能使用execution截取库方法,但仍要使用call切入点,因为调用代码在您的控制之下.
      • 案例B:您向其他用户提供了一个库:让我们假设您的库应使用方面,但库的用户对此一无所知.然后execution切入点将始终有效,因为建议已被编织到您的库的方法中,无论它们是从外部还是从库本身调用的.但是call仅适用于内部呼叫,因为没有方面代码被编织到用户的呼叫代码中.
      • Case A: You use a 3rd party library: Let us assume you cannot (or do not want to) weave aspects into the library. Then you cannot use execution for intercepting library methods, but still use call pointcuts because the calling code is under your control.
      • Case B: You provide a library to other users: Let us assume your library should use aspects, but the library's user does not know anything about it. Then execution pointcuts will always work because the advices are already woven into your library's methods, no matter if they are called from outside or from the library itself. But call would only work for internal calls because no aspect code was woven into the user's calling code.

      仅当您控制调用以及被调用(执行)的代码时,才使用callexecution并没有太大的区别.但是请稍等,仍然有所不同:execution只是编织在一个地方,而call它编织在可能的许多地方,因此对于execution来说,生成的代码量较小.

      Only if you control the calling as well as the called (executed) code it does not make so much difference whether you use call or execution. But wait a minute, it still makes a difference: execution is just woven in one place while call it woven into potentially many places, so the amount of code generated is smaller for execution.

      更新:

      根据要求,这里有一些伪代码:

      Here is some pseudo code, as requested:

      让我们假设我们有一个类MyClass,该类要进行方面增强(通过插入源代码):

      Let us assume we have a class MyClass which is to be aspect-enhanced (via source code insertion):

      class MyClass {
          method foo() {
              print("foo");
              bar();
          }
      
          method bar() {
              print("bar");
              zot();
          }
      
          method zot() {
              print("zot");
          }
      
          static method main() {
              new McClass().foo();
          }
      }
      

      现在,如果我们使用call()

      aspect CallAspect {
          before() : call(* *(..)) {
              print("before " + thisJoinPoint);
          }
      }
      

      在我们的代码上,经过源代码编织后,它看起来像这样:

      upon our code, it would look like this after source code weaving:

      class MyClass {
          method foo() {
              print("foo");
              print("before call(MyClass.bar())");
              bar();
          }
      
          method bar() {
              print("bar");
              print("before call(MyClass.zot())");
              zot();
          }
      
          method zot() {
              print("zot");
          }
      
          static method main() {
              print("before call(MyClass.foo())");
              new McClass().foo();
          }
      }
      

      或者,如果我们使用execution()

      aspect ExecutionAspect {
          before() : execution(* *(..)) {
              print("before " + thisJoinPoint);
          }
      }
      

      在我们的代码上,经过源代码编织后,它看起来像这样:

      upon our code, it would look like this after source code weaving:

      class MyClass {
          method foo() {
              print("before execution(MyClass.foo())");
              print("foo");
              bar();
          }
      
          method bar() {
              print("before execution(MyClass.bar())");
              print("bar");
              zot();
          }
      
          method zot() {
              print("before execution(MyClass.zot())");
              print("zot");
          }
      
          static method main() {
              print("before execution(MyClass.main())");
              new McClass().foo();
          }
      }
      

      您现在看到区别了吗?请注意代码被编入的 和打印声明中的 .

      Can you see the difference now? Pay attention to where the code is woven into and what the print statements say.

      这篇关于PHP中调用和执行切入点之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆