请解释一下,为什么代理上的自调用是在目标上执行的,而不是在代理上执行的?如果这是故意的,那为什么?如果代理是通过子类化创建的,那么在每次方法调用之前都有可能执行一些代码,甚至在自调用时也是如此。我试过了,我有一个自我调用的代理
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?
1条答案
按热度按时间sqxo8psd1#
请阅读Spring手册中的这一章,你就会明白了。这里甚至使用了“自我调用”这个词。如果你仍然不明白,请随时提出后续问题,只要是在上下文中。
更新:好的,在我们确定你真的读了那一章之后,在重新阅读了你的问题并分析了你的代码之后,我发现这个问题实际上是相当深刻的(我甚至投了更高的票),值得更详细地回答。
你对它的工作原理的(错误的)假设
您的误解是动态代理如何工作,因为它们不像示例代码中那样工作。让我将对象id(哈希代码)添加到日志输出中,以便对您自己的代码进行说明:
控制台日志:
看到了吗?总是有相同的对象id,这并不奇怪。由于多态性,您的“代理”(实际上不是代理,而是静态编译的子类)的自调用工作正常。这由java编译器负责。
它是如何工作的
现在请记住,我们在这里讨论的是动态代理,即在运行时创建的子类和对象:
jdk代理为实现接口的类工作,这意味着实现这些接口的类是在运行时创建的。在这种情况下,无论如何都没有超类,这也解释了为什么它只适用于公共方法:接口只有公共方法。
cglib代理也适用于没有实现任何接口的类,因此也适用于受保护和包作用域的方法(但不是私有方法,因为您不能重写这些方法,因此术语private)。
然而,关键的一点是,在上述两种情况下,当代理被创建时,原始对象已经(并且仍然)存在,因此不存在多态性。这种情况是,我们有一个动态创建的代理对象委托给原始对象,即我们有两个对象:一个代理和一个委托。
我想这样说明:
看到区别了吗?因此,控制台日志更改为:
这是springaop或spring的其他部分使用动态代理的行为,甚至是使用jdk或cglib代理的非spring应用程序。
这是特性还是限制?作为aspectj(不是SpringAOP)用户,我认为这是一个限制。也许其他人会认为这是一个特性,因为根据spring中实现代理使用的方式,原则上,您可以在运行时动态(取消)注册方面建议或拦截器,即每个原始对象(delegate)有一个代理,但是对于每个代理,都有一个在调用委托的原始方法之前和/或之后调用的拦截器的动态列表。在非常动态的环境中,这是一件好事。我不知道你会多久用一次。但在aspectj中,您也有
if()
切入点指示符,您可以使用它在运行时确定是否应用某些通知(用于拦截器的aop语言)。