java - 为什么 cglib 不代理 super 调用?

标签 java interceptor cglib

我在下面有以下结构(我使用注释 @Intercepted 来指示被拦截的方法):当我调用被拦截的方法作为 intercepted() 而不使用 super 关键字拦截器按预期调用。但是,当以以下方式调用 super.intercepted() 时,永远不会调用拦截。为什么会这样?

public class Base {
   @Intercepted
   public void intercepted() {}
}

public class BaseImpl extends Base {    
   public void doSomething() {
     super.intercepted(); //<-- does not work
     intercepted(); //<--- without the super, it works
   }
}

最佳答案

答案在于生成的字节码,Java编译器为super.method生成的字节码与this.method相比,在字节码中由 cglib 为代理生成。为了拦截一个方法,cglib 将一个新类添加到类型层次结构中。您的代理类的所有实例将是不同的运行时类型,它是您的代理类的子类。对于您的示例,这会产生类似于以下的类型层次结构:

class Base { 
  void intercepted() { ... }
}

class BaseImpl extends Base { 
  void doSomething() { ... }
}

class BaseImpl$$cglib extends BaseImpl { 
  @Override void intercepted() { ... }
}

现在 Java 编译器就位了。 Java 编译器通过字节码指令之一调用方法。这些指令中的每一个都会导致不同的运行时行为。这里重要的两条指令是最常见的:

  • INVOKEVIRTUAL:调用 this.intercepted 被 Java 编译器转换为动态/虚拟方法调用。作为动态调用的结果,Java 运行时将查看当前类型的虚方法表,以确定要调用哪个方法。方法是bound dynamically .这意味着,如果您从 BaseImpl$$cglib 实例调用 intercepted,则所选方法将为 BaseImpl$$cglib.intercepted。如果您从 BaseImpl$$cglib 类型调用方法,则调用的方法将改为 Base.intercepted,因为 BaseImpl 不会覆盖 拦截。显然,Base.intercepted 会调用自己类中定义的方法。

  • INVOKESTATIC:静态调用在运行时未动态绑定(bind)。为了调用super.intercepted,Java 运行时应该调用父类(super class)型的虚方法表 中的方法。这意味着,BaseImpl 将显式引用 Base 的方法表。因此,BaseImpl$$cglib 的虚方法表永远不会被查阅,调用也无法被拦截。这与无法拦截静态方法的原因基本相同。在字节代码中,静态方法和 super 调用的处理方式完全相同。

也就是说,对于 super.intercepted,cglib 代理永远不会被命中。在 Java 中检测加载的类是不可能的(好吧,你可以 push this rule 一点点),因此没有代理框架可以真正做你想做的事。

关于java - 为什么 cglib 不代理 super 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20131694/

相关文章:

java - spring Interceptor 的 prehandle 方法中的对象参数..它包含什么?

java - 如何在运行时获取远程java类文件字节流?

java - Spring JDK 动态代理和 CGLIB - 实现细节

Java拦截器读取请求体使请求为空

spring - 拦截器或过滤器

java - 奇怪的空指针异常

java - 邻接列表 HashMap<String, ArrayList<Edge>> 无法找到其值

java - 如何使用代码生成跟踪 POJO 的状态

java - 强制终止我不是用 Java 编写的线程

java - Swing组件占用大量内存