Пожалуйста, прочитайте эту главу в руководстве по Spring, тогда вы поймете. Здесь даже используется термин «самообращение». Если вы все еще не понимаете, не стесняйтесь задавать дополнительные вопросы, если они находятся в контексте.
Обновление: Хорошо, теперь, когда мы установили, что вы действительно прочитали эту главу, и после перечитывания вашего вопроса и анализа вашего кода я вижу, что вопрос на самом деле довольно глубокий (я даже проголосовал за него) и стоит ответить более подробно.
Ваше (ложное) предположение о том, как это работает
Ваше недоразумение связано с тем, как работают динамические прокси, потому что они не работают так, как в вашем примере кода. Позвольте мне добавить идентификатор объекта (хеш-код) к выходу журнала для иллюстрации к вашему собственному коду:
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
См? Всегда есть один и тот же идентификатор объекта, что неудивительно. Самовывоз для вашего «прокси» (который на самом деле не прокси, а статически скомпилированный подкласс) работает из-за полиморфизма . Об этом позаботится компилятор Java.
Как это действительно работает
Теперь, пожалуйста, помните, что мы говорим о динамических прокси здесь, то есть подклассах и объектах, созданных во время выполнения:
- Прокси JDK работают для классов, реализующих интерфейсы, что означает, что классы, реализующие эти интерфейсы, создаются во время выполнения. В этом случае суперкласс в любом случае отсутствует, что также объясняет, почему он работает только для открытых методов: интерфейсы имеют только открытые методы.
- Прокси-серверы CGLIB также работают для классов, не реализующих никаких интерфейсов, и, таким образом, также работают с защищенными и пакетно-ориентированными методами (не частными, хотя, поскольку вы не можете переопределить их, таким образом, термин private).
- Важным моментом, однако, является то, что в обоих вышеупомянутых случаях исходный объект уже (и до сих пор) существует при создании прокси, таким образом, не существует такой вещи, как полиморфизм . Ситуация такова, что у нас есть динамически созданный прокси-объект, делегирующий исходный объект, то есть у нас есть два объекта: прокси и делегат .
Я хочу проиллюстрировать это так:
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
Такое поведение вы видите в Spring AOP или других частях Spring, использующих динамические прокси, или даже в приложениях, отличных от Spring, использующих прокси JDK или CGLIB в целом.
Это функция или ограничение? Я, как пользователь AspectJ (не Spring AOP), считаю, что это ограничение. Может быть, кто-то еще может подумать, что это особенность, потому что из-за способа использования прокси в Spring вы можете в принципе (не) регистрировать советы по аспектам или перехватчики динамически во время выполнения, то есть у вас есть один прокси на исходный объект (делегат), но для каждого прокси есть динамический список перехватчиков, вызываемых до и / или после вызова исходного метода делегата. Это может быть хорошо в очень динамичных условиях. Я понятия не имею, как часто вы можете использовать это. Но в AspectJ у вас также есть if()
указатель pointcut, с помощью которого вы можете определить во время выполнения, применять ли определенные советы (язык AOP для перехватчиков) или нет.