请解释一下,为什么对目标执行代理的自我调用而不执行代理?如果那是故意的,那为什么呢?如果代理是通过子类创建的,则即使在自调用时,也可能在每个方法调用之前执行一些代码。我试过了,我有自己的代理权
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?
答案 0 :(得分:1)
请阅读Spring手册中的this chapter,您将了解。甚至在这里使用了“自我调用”一词。如果您仍然不理解,请随时提出后续问题,只要它们在上下文中即可。
更新:好的,现在,在我们确定您确实阅读了本章之后,并且在重新阅读了您的问题并分析了您的代码之后,我发现该问题实际上非常深刻(我什至赞成它),并且值得更详细地回答。
您的误解是关于动态代理的工作方式,因为它们不像您的示例代码中那样工作。让我将对象ID(哈希码)添加到日志输出中,以说明您自己的代码:
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编译器负责解决此问题。
现在,请记住我们在这里谈论的是动态代理,即在运行时创建的子类和对象:
我想这样说明:
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();
}
}
看到区别了吗?因此,控制台日志更改为:
de.scrum_master.app.DelegatingPrinterProxy@59f95c5d Before print both
de.scrum_master.app.DummyPrinter@5c8da962 print1
de.scrum_master.app.DummyPrinter@5c8da962 print2
这是使用动态代理甚至通常使用JDK或CGLIB代理的非Spring应用程序在Spring AOP或Spring其他部分看到的行为。
这是功能还是限制?我作为AspectJ(不是Spring AOP)用户认为这是一个限制。也许其他人可能认为这是一个功能,因为由于在Spring中实现了代理用法,因此您原则上可以在运行时动态地(取消)注册方面建议或拦截器,即,每个原始对象(代理)有一个代理,但是对于每个代理,在调用代理的原始方法之前和/或之后都有一个动态的拦截器列表。在非常动态的环境中,这可能是一件好事。我不知道您可能要多久使用一次。但是在AspectJ中,您还可以使用if()
切入点指示符,可以在运行时确定是否使用某些建议(拦截器的AOP语言)。