当前位置: 首页 > 知识库问答 >
问题:

PHP中调用和执行切入点的区别?

陆正德
2023-03-14

在Java中的AOP(AspectJ)中,当我们谈到方法切入点时,我们可以将它们区分为两个不同的集合:方法调用切入点方法执行切入点

基于这里的这些资源:

  • 执行vs.调用连接点
  • AOP中调用和执行的区别
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
    }
}
    null
pointcut methodCallPointcut(): 
   call(void com.example.CompiletimeTypeObject.someMethodOfTarget())

将匹配callerObject.SomeMethod()方法中的target.someMethod();连接点,因为RuntimeTypeObject的编译类型是CompiletimeTypeObject,但是此方法调用pointcut:

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

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

  • 方法执行切入点指的是方法的执行(即在方法被调用之后或在方法调用返回之前)。它不提供有关调用方的信息,更重要的是,它引用对象的运行时类型,而不是编译时类型。
pointcut methodCallPointcut(): 
       execution(void com.example.CompiletimeTypeObject.someMethodOfTarget())

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

因为匹配是基于对象的运行时类型的,对于两者都是RuntimeTypeObject,并且RuntimeTypeObject同时是CompiletimeTypeObject(第一个切入点)和RuntimeTypeObject(第二个切入点)。

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

感谢您的关注!

在A(我使用第三方库)的情况下,我实际上不能截获库方法的执行,因为库本身已经编译成字节码,而且与该库有关的任何方面也已经编织到字节码中(为了这样做,我需要编织源代码)。

因此,我只能截获对库方法的方法调用,但同样,我只能截获代码中对库方法的调用,而不能截获库本身内部对库方法的调用,因为同样的原理(库本身内部对库方法的调用也已经编译)。

这同样适用于系统类(原理相同),正如这里所说(即使引用引用了JBoss):

不能在执行表达式中使用系统类,因为不可能对它们进行仪表化。

在情况B中(我为其他用户提供一个库),如果我确实需要在库本身或将来使用该方法的用户代码中截获我的库的某个方法的使用情况,然后我需要使用一个执行切入点,因为方面weaver将编译涉及我的库的方法执行和调用切入点,而不是使用我的库方法的用户代码(只是因为在我编写库时用户代码还不存在),因此使用一个执行切入点将确保编织将发生在方法执行内部(为了一个清晰直观的示例,请查看下面的@kriegaex伪代码),而不是发生在我的库中调用方法的任何地方(即在调用方)。

因此,我可以截获我的库方法的使用情况(更准确地说,是执行情况),无论是在我的库中还是在用户代码中使用该方法。如果我在本例中使用了方法调用pointcut,我将只截获从我的库中发出的调用,而不截获在用户代码中发出的调用。

共有1个答案

缪嘉志
2023-03-14

免责声明:我不会说PHP,甚至一点也不会。因此,我的回答本质上是比较笼统的,而不是针对PHP的。

AFAIK,PHP是一种解释语言而不是编译语言。所以区别不在于编译时类型和运行时类型,而在于语义上声明的类型和实际类型。我认为基于PHP的AOP框架不会“编译”任何东西,而是预处理源代码,将额外的(方面)源代码注入原始文件。也许仍有可能以某种方式将声明类型与实际类型区分开来。

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

    null

以下是一些伪代码:

让我们假设我们有一个类MyClass,它将被方面增强(通过源代码插入):

class MyClass {
    method foo() {
        print("foo");
        bar();
    }

    method bar() {
        print("bar");
        zot();
    }

    method zot() {
        print("zot");
    }

    static method main() {
        new McClass().foo();
    }
}

现在,如果我们像这样使用callaspect应用callaspect

aspect CallAspect {
    before() : call(* *(..)) {
        print("before " + thisJoinPoint);
    }
}
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();
    }
}
aspect ExecutionAspect {
    before() : execution(* *(..)) {
        print("before " + thisJoinPoint);
    }
}
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();
    }
}
 类似资料:
  • 我试图尽可能简单地理解AOP中执行和调用之间的区别。据我所知,execution()将在执行代码中添加一个连接点,因此在本例中,但是如果切入点是,那么连接点将是。这是正确的吗?

  • PostSharp中是否有相当于AeyJ调用切入点? 具体:ClassA和ClassB都在ClassC上调用方法foo()。我只想拦截从A到C的调用,而不是从B到C的调用。在AspectJ中,这可以通过将调用与如下所示的切入点配对来实现: 调用(*ClassC.foo()) 我如何在PostSharp中实现这一点?

  • 问题内容: 没有迅速的方法。该程序必须从某处开始执行。那么快速代码执行的切入点是什么,它是如何确定的? 问题答案: 普通Swift模块中的入口点是模块中名为的文件。是唯一一个允许在顶层具有表达式和语句的文件(模块中的所有其他Swift文件只能包含声明)。 可可触摸使用属性上的实现,而不是一个纪念的入口点文件。可可曾经使用了一个简单的最小文件,但是从Xcode 6.1开始, 它在的实现上使用属性。

  • 使用“Eclipse memory Analyzer”分析了应用程序服务器的“堆转储”之后,我发现我的应用程序消耗了450 MB,而”类的实例消耗了这450MB中的30%. 的每个实例(大约)占用6 MB,这是因为每个实例都管理与java.lang.Reflect.Method实例匹配的缓存,而且令人惊讶的是,缓存了很多java方法(我的pointcut表达式没有提到的方法)。 在阅读了Sprin

  • 问题内容: 我是新来的春天,具有hibernate的工作知识。我的工作是通过使用Spring声明式方法来实现交易。由于Google的帮助,我成功地在Google的帮助下完成了交易。但是无法清楚地了解我在application- context.xml中使用的术语。 1。 有人可以向我解释以上几点,与此同时,我也试图从google上了解它。 问题答案: 您已经成功实施了, 在我们可以通过三种方式实现

  • 我有两个不同的方面类来计算测试程序执行的非静态方法调用的数量。第一个方面统计“调用”连接点上的方法: 第二个方面统计“执行”连接点上的方法: 是一个静态方法在计数器类。 小测试程序的方法调用次数相同。但是当我用一个更大的程序改变测试程序时,第二个方面类(带有执行切入点)中的方法调用数量比带有调用切入点的方面类中的方法调用数量多。这是合理的,因为呼叫连接点不会挑出用超级发出的呼叫,因此不会计算它们。