为什么自调用不适用于 Spring 代理(例如使用 AOP)? [英] Why does self-invocation not work for Spring proxies (e.g. with AOP)?

查看:71
本文介绍了为什么自调用不适用于 Spring 代理(例如使用 AOP)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请解释一下,为什么在目标上执行代理上的自调用而不是代理?如果是故意的,那又是为什么呢?如果通过子类创建代理,则可以在每次方法调用之前执行一些代码,即使在自调用时也是如此.我试过了,我有自调用代理

Please explain, why self invocation on proxy performed on target but not proxy? If that made on purpose, then why? If proxies created by subclassing, it's possible to have some code executed before each method call, even on self invocation. I tried, and I have proxy on self invocation

public class DummyPrinter {
    public void print1() {
        System.out.println("print1");
    }

    public void print2() {
        System.out.println("print2");
    }

    public void printBoth() {
        print1();
        print2();
    }
}

public class PrinterProxy extends DummyPrinter {
    @Override
    public void print1() {
        System.out.println("Before print1");
        super.print1();
    }

    @Override
    public void print2() {
        System.out.println("Before print2");
        super.print2();
    }

    @Override
    public void printBoth() {
        System.out.println("Before print both");
        super.printBoth();
    }
}

public class Main {
    public static void main(String[] args) {
        DummyPrinter p = new PrinterProxy();
        p.printBoth();
    }
}

输出:

Before print both
Before print1
print1
Before print2
print2

这里每个方法调用代理.为什么在文档中提到在自调用的情况下应该使用 AspectJ?

Here each method called on proxy. Why in documentation mentioned that AspectJ should be used in case of self invocation?

推荐答案

请阅读 这一章,然后你就明白了.甚至在那里使用了术语自调用".如果您仍然不明白,请随时提出后续问题,只要它们在上下文中即可.

Please read this chapter in the Spring manual, then you will understand. Even the term "self-invocation" is used there. If you still do not understand, feel free to ask follow-up questions, as long as they are in context.

更新:好的,现在我们已经确定你真的阅读了那一章,在重新阅读你的问题并分析你的代码后,我发现这个问题实际上非常深刻(我什至赞成它) 并值得更详细地回答.

Update: Okay, now after we have established that you really read that chapter and after re-reading your question and analysing your code I see that the question is actually quite profound (I even upvoted it) and worth answering in more detail.

您的误解是关于动态代理的工作方式,因为它们不像您的示例代码那样工作.让我将对象 ID(哈希码)添加到日志输出中,以便在您自己的代码中进行说明:

Your misunderstanding is about how dynamic proxies work because they do not work as in your sample code. Let me add the object ID (hash code) to the log output for illustration to your own code:

package de.scrum_master.app;

public class DummyPrinter {
  public void print1() {
    System.out.println(this + " print1");
  }

  public void print2() {
    System.out.println(this + " print2");
  }

  public void printBoth() {
    print1();
    print2();
  }
}

package de.scrum_master.app;

public class PseudoPrinterProxy extends DummyPrinter {
  @Override
  public void print1() {
    System.out.println(this + " Before print1");
    super.print1();
  }

  @Override
  public void print2() {
    System.out.println(this + " Before print2");
    super.print2();
  }

  @Override
  public void printBoth() {
    System.out.println(this + " Before print both");
    super.printBoth();
  }

  public static void main(String[] args) {
    new PseudoPrinterProxy().printBoth();
  }
}

控制台日志:

de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print both
de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print1
de.scrum_master.app.PseudoPrinterProxy@59f95c5d print1
de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print2
de.scrum_master.app.PseudoPrinterProxy@59f95c5d print2

看到了吗?总是有相同的对象 ID,这并不奇怪.由于多态,您的代理"(它不是真正的代理,而是静态编译的子类)的自调用有效.这由 Java 编译器负责.

See? There is always the same object ID, which is no surprise. Self-invocation for your "proxy" (which is not really a proxy but a statically compiled subclass) works due to polymorphism. This is taken care of by the Java compiler.

现在请记住,我们在这里讨论的是动态代理,即在运行时创建的子类和对象:

Now please remember we are talking about dynamic proxies here, i.e. subclasses and objects created during runtime:

  • JDK 代理适用于实现接口的类,这意味着实现这些接口的类是在运行时创建的.在这种情况下,无论如何都没有超类,这也解释了为什么它只适用于公共方法:接口只有公共方法.
  • CGLIB 代理也适用于未实现任何接口的类,因此也适用于受保护和包范围的方法(但不是私有方法,因为您无法覆盖这些方法,因此称为私有).
  • 关键的一点是,在上述两种情况下,在创建代理时原始对象已经(并且仍然)存在,因此没有多态性这样的东西.情况是我们有一个动态创建的代理对象委托给原始对象,即我们有两个对象:代理和委托.
  • JDK proxies work for classes implementing interfaces, which means that classes implementing those interfaces are being created during runtime. In this case there is no superclass anyway, which also explains why it only works for public methods: interfaces only have public methods.
  • CGLIB proxies also work for classes not implementing any interfaces and thus also work for protected and package-scoped methods (not private ones though because you cannot override those, thus the term private).
  • The crucial point, though, is that in both of the above cases the original object already (and still) exists when the proxies are created, thus there is no such thing as polymorphism. The situation is that we have a dynamically created proxy object delegating to the original object, i.e. we have two objects: a proxy and a delegate.

我想这样说明:

package de.scrum_master.app;

public class DelegatingPrinterProxy extends DummyPrinter {
  DummyPrinter delegate;

  public DelegatingPrinterProxy(DummyPrinter delegate) {
    this.delegate = delegate;
  }

  @Override
  public void print1() {
    System.out.println(this + " Before print1");
    delegate.print1();
  }

  @Override
  public void print2() {
    System.out.println(this + " Before print2");
    delegate.print2();
  }

  @Override
  public void printBoth() {
    System.out.println(this + " Before print both");
    delegate.printBoth();
  }

  public static void main(String[] args) {
    new DelegatingPrinterProxy(new DummyPrinter()).printBoth();
  }
}

看到区别了吗?因此控制台日志更改为:

See the difference? Consequently the console log changes to:

de.scrum_master.app.DelegatingPrinterProxy@59f95c5d Before print both
de.scrum_master.app.DummyPrinter@5c8da962 print1
de.scrum_master.app.DummyPrinter@5c8da962 print2

这是您在使用动态代理的 Spring AOP 或 Spring 的其他部分或什至使用 JDK 或 CGLIB 代理的非 Spring 应用程序中看到的行为.

This is the behaviour you see with Spring AOP or other parts of Spring using dynamic proxies or even non-Spring applications using JDK or CGLIB proxies in general.

这是功能还是限制?我作为 AspectJ(不是 Spring AOP)用户认为这是一个限制.也许其他人可能认为这是一项功能,因为由于在 Spring 中实现代理使用的方式,您原则上可以(取消)在运行时动态注册方面建议或拦截器,即每个原始对象(委托)都有一个代理,但是对于每个代理,都有一个在调用委托的原始方法之前和/或之后调用的拦截器的动态列表.这在非常动态的环境中可能是一件好事.我不知道你多久想使用它.但是在 AspectJ 中,您还有 if() 切入点指示符,您可以使用它在运行时确定是否应用某些通知(用于拦截器的 AOP 语言).

Is this a feature or a limitation? I as an AspectJ (not Spring AOP) user think it is a limitation. Maybe someone else might think it is a feature because due to the way proxy usage is implemented in Spring you can in principle (un-)register aspect advices or interceptors dynamically during runtime, i.e. you have one proxy per original object (delegate), but for each proxy there is a dynamic list of interceptors called before and/or after calling the delegate's original method. This can be a nice thing in very dynamic environments. I have no idea how often you might want to use that. But in AspectJ you also have the if() pointcut designator with which you can determine during runtime whether to apply certain advices (AOP language for interceptors) or not.

这篇关于为什么自调用不适用于 Spring 代理(例如使用 AOP)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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